package pl.softech.gpw.candles.editor.xml;

import java.awt.Point;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Comparator;
import java.util.TreeSet;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

import pl.softech.gpw.candles.Candle;
import pl.softech.gpw.candles.editor.CandlestickWidget;
import pl.softech.gpw.candles.editor.PaletteCandelstickWidgets;
import pl.softech.gpw.candles.editor.meta.CandlestickPatternMetaDataImpl;
import pl.softech.gpw.candles.patterns.ICandlePatternMetaData;
import pl.softech.gpw.model.FInstrument;
import pl.softech.gpw.model.PropertyName;

public class XmlDefinitionLoader {

    private CandlestickWidget[] candlestickWidgets;
    private ICandlePatternMetaData metaData;

    public XmlDefinitionLoader load(File file) throws Exception {

        BufferedInputStream in = null;

        try {
            in = new BufferedInputStream(new FileInputStream(file));
            loadXml(in);
        } finally {
            if(in != null) {
                in.close();
            }
        }

        return this;

    }

    private void loadXml(InputStream in) throws Exception {

        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document document = builder.parse(in);

        Element element = document.getDocumentElement();

        if (!XmlDefinitionBuilder.ROOT_ELEMENT_NAME.equals(element.getNodeName())) {
            throw new Exception("No" + XmlDefinitionBuilder.ROOT_ELEMENT_NAME + " root element");
        }

        metaData = readMetaData(element);

        candlestickWidgets = new CandlestickWidget[metaData.getBarCount()];

        int index = 0;
        for (Node node = element.getFirstChild(); node != null; node = node.getNextSibling()) {
            if (node.hasAttributes()) {
                CandlestickWidget cw = readCandlestick(node);
                candlestickWidgets[index++] = cw;
            }
        }

    }

    public CandlestickWidget[] getCandlestickWidgets() {
        return candlestickWidgets;
    }

    public ICandlePatternMetaData getMetaData() {
        return metaData;
    }

    private Method getSetterForGetter(Class<?> clazz, Method getter) throws Exception {

        String name = "set".concat(getter.getName().substring("get".length()));

        return clazz.getMethod(name, getter.getReturnType());

    }

    private CandlestickWidget readCandlestick(Node node) throws Exception {

        FInstrument fin = new FInstrument();

        NamedNodeMap nnm = node.getAttributes();

        for (Method m : FInstrument.class.getMethods()) {

            if (m.getParameterTypes().length > 0) {
                continue;
            }

            PropertyName meta = m.getAnnotation(PropertyName.class);

            if (meta == null) {
                continue;
            }

            String att = nnm.getNamedItem(meta.name()).getNodeValue();

            if (m.getReturnType() == float.class) {
                float value = Float.parseFloat(att);
                Method setter = getSetterForGetter(FInstrument.class, m);
                setter.invoke(fin, value);
            }

        }

        CandlestickWidget cw = new CandlestickWidget(new Candle(fin));
        
        loadBoundsAttributes(nnm, cw);
        
        return cw;
    }
    
    private void loadBoundsAttributes(NamedNodeMap nnm, CandlestickWidget cw) throws Exception {
        
        BoundsAttributes bounds = new BoundsAttributes();
        
        for (Method m : BoundsAttributes.class.getMethods()) {

            if (m.getParameterTypes().length > 0) {
                continue;
            }

            PropertyName meta = m.getAnnotation(PropertyName.class);

            if (meta == null) {
                continue;
            }

            String att = nnm.getNamedItem(meta.name()).getNodeValue();

            if (m.getReturnType() == int.class) {
                int value = Integer.parseInt(att);
                Method setter = getSetterForGetter(BoundsAttributes.class, m);
                setter.invoke(bounds, value);
            }

        }
        
        cw.getWidget().setBounds(bounds.getBounds());
        
    }
    
    private ICandlePatternMetaData readMetaData(Element element) throws Exception {
        CandlestickPatternMetaDataImpl ret = new CandlestickPatternMetaDataImpl();

        for (Method m : ICandlePatternMetaData.class.getMethods()) {

            if (m.getParameterTypes().length > 0) {
                continue;
            }

            PropertyName meta = m.getAnnotation(PropertyName.class);
            if (meta == null) {
                continue;
            }

            String att = element.getAttribute(meta.name());
            if (m.getReturnType().isEnum()) {

                for (Object value : m.getReturnType().getEnumConstants()) {

                    if (att.equals(value.toString())) {
                        Method setter = getSetterForGetter(CandlestickPatternMetaDataImpl.class, m);
                        setter.invoke(ret, value);
                    }

                }

            } else if (m.getReturnType() == int.class) {

                int num = Integer.parseInt(att);
                Method setter = getSetterForGetter(CandlestickPatternMetaDataImpl.class, m);
                setter.invoke(ret, num);

            }

        }

        return ret;
    }

    public static void main(String[] args) throws Exception {
        TreeSet<CandlestickWidget> candlestickWidgets = new TreeSet<CandlestickWidget>(
                new Comparator<CandlestickWidget>() {

                    @Override
                    public int compare(CandlestickWidget o1, CandlestickWidget o2) {

                        return (int) (o1.getWidget().getBounds().getX() - o2.getWidget().getBounds().getX());
                    }
                });

        int x = 0;
        for (PaletteCandelstickWidgets w : PaletteCandelstickWidgets.values()) {
            CandlestickWidget widget = w.widget();
            widget.setLocation(new Point(x++, 10));
            candlestickWidgets.add(widget);
        }

        ICandlePatternMetaData meta = new ICandlePatternMetaData() {

            @Override
            public Type getType() {
                return Type.REVERSAL;
            }

            @Override
            public int getBarCount() {
                return 3;
            }

            @Override
            public Direction getDirection() {
                return Direction.BULLISH;
            }

            @Override
            public Reliability getReliability() {
                return Reliability.STRONG;
            }

        };

        XmlDefinitionBuilder builder = new XmlDefinitionBuilder(candlestickWidgets, meta);

        System.out.println(builder.build().getXmlContent());

        ByteArrayInputStream in = new ByteArrayInputStream(builder.getXmlContent().getBytes());

        XmlDefinitionLoader loader = new XmlDefinitionLoader();
        loader.loadXml(in);

        builder = new XmlDefinitionBuilder(Arrays.asList(loader.getCandlestickWidgets()), loader.getMetaData());
        System.out.println(builder.build().getXmlContent());
    }

}
